#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/8/5 14:31
# @Author : luhongxuan
# @Site :
# @File : auth.py
# @Software: PyCharm

import copy
import json
import traceback

from django.apps import apps
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.http import Http404
from django.utils.translation import ugettext_lazy as _
from rest_framework import exceptions, status
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError, MethodNotAllowed
from rest_framework.response import Response
from rest_framework.views import set_rollback, APIView
from rest_framework.viewsets import ModelViewSet

from converter import Converter
from data import CHECK_FOREIGN_KEY, CHECK_UNIQUE, CHECK_NOT_NULL, CHECK_CHOICES, CHECK_IP_ADDRESS, \
    ESCAPE_CHARACTER, CHECK_BOOLEAN
from list import ListHandler
from log import logger_default, logger_error
from uino_rest_framework.pagination import ZetPagination
from validators import is_boolean, is_float, ip_validator


class BatchImportLog(object):
    """
    用于存放批量导入结果集。
    """

    def __init__(self):
        self.all_count = 0
        self.success_count = 0
        self.fail_count = 0
        self.err_msg = []
        self.response_data = []

    def obj_2_json(self, arg):
        return {
            'all_count': self.all_count,
            'success_count': self.success_count,
            'fail_count': self.fail_count,
            'err_msg': self.err_msg,
            'response_data': self.response_data
        }


class CheckLog(object):
    """
    用于存放校验结果
    """

    def __init__(self):
        self.field_name = ""
        self.value = ""
        self.verbose_name = ""
        self.err = ""

    def obj_2_json(self, arg):
        return {
            'field_name': self.field_name,
            'verbose_name': self.verbose_name,
            'value': self.value,
            'err': self.err,
        }


class ReturnMsg(object):
    """
    统一的接口返回值
    """

    def __init__(self, code=status.HTTP_200_OK, message='success', errors=None, data=None):
        self.code = code
        self.message = message
        self.errors = [] if errors is None else errors
        self.data = [] if data is None else data

    def obj_2_json(self):
        return {
            'code': self.code,
            'message': self.message,
            'errors': self.errors,
            'data': self.data
        }


class ZetAPIView(APIView):

    def process_method(self, request, status_code, func, *args, **kwargs):
        try:
            res = func(request, *args, **kwargs)

            if isinstance(res, dict):
                code = res.get('code', status_code)
                data = res.get('data', res)
                message = res.get('message', 'success')
                errors = res.get('errors', [])
            elif isinstance(res, str) or isinstance(res, list) or isinstance(res, tuple):
                code = status_code
                data = res
                message = 'success'
                errors = []
            elif isinstance(res, int):
                if res == status.HTTP_405_METHOD_NOT_ALLOWED:
                    return self.http_method_not_allowed(request, *args, **kwargs)
                code = status_code
                data = res
                message = 'success'
                errors = []
            else:
                try:
                    code = status_code
                    data = None if res is None else str(res)
                    message = 'success'
                    errors = []
                except:
                    raise Exception('不支持的返回值类型')
        except MethodNotAllowed as e:
            res = None
            code = status.HTTP_405_METHOD_NOT_ALLOWED
            data = []
            message = 'failed:{}'.format(str(e))
            errors = str(e)
        except Exception as e:
            res = None
            logger_error.error(traceback.format_exc())
            code = status.HTTP_500_INTERNAL_SERVER_ERROR
            data = []
            message = 'failed:{}'.format(str(e))
            errors = str(e)
        resp = {'code': code,
                'data': data,
                'message': message,
                'errors': errors
                }
        if isinstance(res, dict) and 'data' in resp:
            resp.update(res)

        return Response(resp, status=code)

    def get_data(self, request, *args, **kwargs):
        """
         重写该方法，进行数据逻辑处理
        :param request:
        :param args:
        :param kwargs:
        :return: 返回值支持的类型
        1、字符串
        2、字典： {'code':'...','data':'...','message':'...'}
         3、列表：[1,2,3]
        """

        return status.HTTP_405_METHOD_NOT_ALLOWED

    def post_data(self, request, *args, **kwargs):
        """
         重写该方法，进行数据逻辑处理
        :param request:
        :param args:
        :param kwargs:
        :return: 返回值支持的类型
        1、字符串
        2、字典： {'code':'...','data':'...','message':'...'}
         3、列表：[1,2,3]
        """

        return status.HTTP_405_METHOD_NOT_ALLOWED

    def delete_data(self, request, *args, **kwargs):
        """
         重写该方法，进行数据逻辑处理
        :param request:
        :param args:
        :param kwargs:
        :return: 返回值支持的类型
        1、字符串
        2、字典： {'code':'...','data':'...','message':'...'}
         3、列表：[1,2,3]
        """

        return status.HTTP_405_METHOD_NOT_ALLOWED

    def put_data(self, request, *args, **kwargs):
        """
         重写该方法，进行数据逻辑处理
        :param request:
        :param args:
        :param kwargs:
        :return: 返回值支持的类型
        1、字符串
        2、字典： {'code':'...','data':'...','message':'...'}
         3、列表：[1,2,3]
        """

        return status.HTTP_405_METHOD_NOT_ALLOWED

    def patch_data(self, request, *args, **kwargs):
        """
         重写该方法，进行数据逻辑处理
        :param request:
        :param args:
        :param kwargs:
        :return: 返回值支持的类型
        1、字符串
        2、字典： {'code':'...','data':'...','message':'...'}
         3、列表：[1,2,3]
        """

        return status.HTTP_405_METHOD_NOT_ALLOWED

    def get(self, request, *args, **kwargs):
        return self.process_method(request, status.HTTP_200_OK, self.get_data, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.process_method(request, status.HTTP_200_OK, self.post_data, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.process_method(request, status.HTTP_200_OK, self.put_data, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.process_method(request, status.HTTP_200_OK, self.patch_data, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.process_method(request, status.HTTP_204_NO_CONTENT, self.delete_data, *args, **kwargs)


class ZetViewSet(ModelViewSet):
    pagination_class = ZetPagination
    batch_create_app_name = ''  # 用于批量导入时外键唯一性转ID
    batch_create_model_name = ''  # 用于批量导入时外键唯一性转ID
    batch_create_identify_code = 'identify_code'  # 前端规定的每条记录的唯一标示，用于定位记录,批量入库时，每条记录都需要有该字段
    batch_check_required_fields = []  # 批量校验时的必填项列表
    batch_check_unique_fields = []  # 逻辑上的唯一行列

    tag_fields = []  # 存放包含tags的列，可能是tags也可能是外键。

    global_search_exclude_models = []  # 全局搜索需要排除的表
    global_search_include_fields = {}
    # 全局搜索 外键模型中可以作为查询条件的字段，配置后，其他字段不作为搜索条件 形如:
    # global_search_include_fields = {
    #    'Interface': ['interface_name','name']
    # }
    global_search_allow_repeat_models = []  # 全局搜索可重复查询的表
    order_replace_fields = {}  # 排序替换列
    # order_replace_fields = {'A': 'B'} A列不支持排序，可以替换为B列

    _f_set_dicts = dict()  # 用于缓存外键列表集合，防止查询多次，影响效率。
    _unique_key_dict = dict()  # 缓存唯一性列
    _filter_table_list = list()  # 缓存已经加入查询条件的表,有的表通过外键关联可能出现多次。
    _pre_process_res = None  # 缓存预处理结果

    def get_queryset(self):
        """
        重写默认的获取queryset的方法，支持自定义查询条件
        :return:
        """
        query_set = None
        search_Query, order_str = self.get_queryset_params()
        if search_Query:
            if order_str:
                query_set = self.model.objects.get_queryset().filter(search_Query).order_by(order_str)
            else:
                query_set = self.model.objects.get_queryset().filter(search_Query)
        else:
            if order_str:
                query_set = self.model.objects.get_queryset().all().order_by(order_str)
            else:
                query_set = self.model.objects.get_queryset().all()
        return query_set

    def get_queryset_params(self):
        """
        获取查询条件
        :return:
        """
        search_filter = {}
        request_para = self.request.query_params
        for k in request_para:
            if k.lower() not in ['pagesize', 'page', 'ordering', 'desc', 'asc']:
                if '[]' in k:
                    new_k = k[:-2]
                    search_filter[new_k] = request_para.getlist(k)
                else:
                    search_filter[k] = request_para.get(k)

        ZetPagination.page_size = self._get_page_size(request_para)

        order_factor = self._get_order_para(request_para)

        search_factor = self.get_search_factor(search_filter)
        return search_factor, order_factor

    def _get_page_size(self, request_para):
        """
        接收前端传递的pagesize参数，当pagesize=-1时，pagesize赋值为最大值。
        :param request_para:
        :return:
        """

        page_size = request_para.get('pagesize')

        if not page_size:
            page_size = request_para.get('pageSize')
            if not page_size:
                page_size = 10
        if page_size == '-1' or page_size == -1:
            page_size = ZetPagination.max_page_size

        return page_size

    def _get_order_para(self, request_para):
        """
        获取排序列，以及排序方式
        :param request_para:
        :return:
        """
        order_info = request_para.get('ordering', '')
        order_type = 'asc'
        order_field = None
        if order_info:
            if order_info.startswith('-'):
                order_type = 'desc'
                order_field = order_info[1:]
            elif order_info.startswith('+'):
                order_type = 'asc'
                order_field = order_info[1:]
            else:
                order_type = 'asc'
                order_field = order_info

        order_field = self.order_replace_fields.get(order_field, order_field)
        order_factor = '-' + order_field if order_type == 'desc' else order_field

        return order_factor

    def pre_process_order_field(self, field_name):
        """
        当前无法直接排序，需要转化为其他列时，使用该方法
        :param field_name:
        :return:
        """
        return field_name

    def record_check_log(self, field_name, val, msg, errors):
        """
        记录校验日志
        :param field_name:
        :param val:
        :param msg:
        :param errors:
        :return:
        """
        check_log = CheckLog()
        check_log.field_name = field_name
        check_log.value = val
        check_log.err = msg
        errors.append(json.dumps(check_log, default=check_log.obj_2_json, ensure_ascii=False))

    def special_search(self, k, v):
        """
        如果存在特殊字段，继承之，重写该方法,处理choices字段 value转key
        :param k:
        :param v:
        :return:
        """
        return k, v

    def get_tags_search_filter(self, tag_str):
        """
        获取tags字段的查询条件，可能有多个tags列，由tag_fields决定。
        ：params tag_str: 可以有多个tag,用英文逗号(,)隔开
        ：return：
        """
        search_filter_tags = None
        tag_list = tag_str.split(',')
        for tag_field in self.tag_fields:
            if not search_filter_tags:
                search_filter_tags = Q()
                search_filter_tags.connector = 'OR'
            if tag_field == 'tags':
                search_filter_tags.children.append(('tags__name__in', tag_list))
            else:
                search_filter_tags.children.append(('%s__tags__name__in' % tag_field, tag_list))

        return search_filter_tags

    def get_search_factor(self, search_filter, contain=True, regex=False):
        search_dict_of_keyword = {}
        search_Q = Q()
        search_filter_tags = None
        if 'search' in search_filter:
            keyword = search_filter['search']
            del search_filter['search']
        else:
            keyword = None
        if 'tags' in search_filter:
            search_filter_tags = self.get_tags_search_filter(search_filter['tags'])
            del search_filter['tags']
        if keyword:
            for field in self.model._meta.concrete_fields:
                field_type = str(type(field))
                if 'Char' in field_type or 'Text' in field_type:
                    field_name, keyword_new = self.special_search(field.name, keyword)
                    if contain:
                        search_dict_of_keyword[field_name + '__icontains'] = keyword_new
                    elif regex:
                        search_dict_of_keyword[field_name + '__iregex'] = keyword_new
                    else:
                        search_dict_of_keyword[field_name] = keyword_new
                elif 'IPAddress' in field_type:
                    # 如果字符串中包含转义符，不进入查询
                    flag = False
                    for char in ESCAPE_CHARACTER:
                        if char in keyword:
                            flag = True
                            break
                    if not flag:
                        search_dict_of_keyword[field.name + '__iregex'] = keyword
                elif 'Boolean' in field_type:
                    if is_boolean(keyword):
                        search_dict_of_keyword[field.name] = Converter.convert_to_boolean(keyword)
                elif 'Decimal' in field_type or 'Float' in field_type:
                    if is_float(keyword):
                        keyword_float = float(keyword)
                        search_dict_of_keyword[field.name] = keyword_float
                elif 'Integer' in field_type:
                    try:
                        keyword_int = int(keyword)
                        search_dict_of_keyword[field.name] = keyword_int
                    except ValueError:
                        pass

                elif 'Foreign' in field_type or 'OneToOne' in field_type:
                    # 外键表中的字符字段作为查询条件。支持无限关联外键。
                    self.get_search_factor_foreign_key(self.model, field, field.name, keyword, search_dict_of_keyword,
                                                       contain, regex)

            search_Q.connector = 'OR'

            for k, v in search_dict_of_keyword.items():
                search_Q.children.append((k, v))
            ZetViewSet._filter_table_list = list()
        if search_filter:
            search_filter_Q = Q()
            search_filter_Q.connector = 'AND'
            for k, v in search_filter.items():
                # 处理特殊字符
                k, v = self.special_search(k, v)
                for field in self.model._meta.concrete_fields:
                    if field.name == k:
                        field_type = str(type(field))
                        if 'IPAddress' in field_type:
                            if not ip_validator(v):  # 输入条件不是标准ip格式时，通过iregex查询
                                if not k.endswith('regex') and not k.endswith('contains'):
                                    k = k + '__iregex'
                        elif 'Boolean' in field_type:
                            v = Converter.convert_to_boolean(v)

                search_filter_Q.children.append((k, v))

        search_filter_temp = None
        if search_dict_of_keyword:
            if search_filter:
                search_filter_temp = search_Q & search_filter_Q
            else:
                search_filter_temp = search_Q

        else:
            if search_filter:
                search_filter_temp = search_filter_Q

        if search_filter_temp:
            if search_filter_tags:
                return search_filter_temp & search_filter_tags
            else:
                return search_filter_temp
        else:
            return search_filter_tags

    def get_search_factor_foreign_key(self, parent_model, field, field_name, keyword, search_dict_of_keyword,
                                      contain=True, regex=False):
        """
        把外键的列也当做查询条件
        :param parent_model:
        :param field:
        :param field_name:
        :param keyword:
        :param search_dict_of_keyword:
        :param contain:
        :param regex:
        :return:
        """
        if field.related_model._meta.object_name in self.global_search_exclude_models:
            return None

        # 判断该表是否已经加入查询条件，如果已经加入，则跳过，可提高查询效率。
        if field.related_model._meta.object_name in ZetViewSet._filter_table_list:
            return None
        elif field.related_model._meta.object_name not in self.global_search_allow_repeat_models:
            ZetViewSet._filter_table_list.append(field.related_model._meta.object_name)
        allow_fields = self.global_search_include_fields.get(field.related_model._meta.object_name)

        if keyword and parent_model != field.related_model:
            for related_field in field.related_model._meta.concrete_fields:
                if allow_fields:
                    if related_field.name not in allow_fields:
                        continue
                related_field_name = '%s__%s' % (field_name, related_field.name)
                field_type = str(type(related_field))
                if 'Char' in field_type or 'Text' in field_type:
                    related_field_name, keyword_new = self.special_search(related_field_name, keyword)
                    if contain:
                        search_dict_of_keyword[related_field_name + '__icontains'] = keyword_new
                    elif regex:
                        search_dict_of_keyword[related_field_name + '__iregex'] = keyword_new
                    else:
                        search_dict_of_keyword[related_field_name] = keyword_new
                elif 'IPAddress' in field_type:
                    flag = False
                    # 如果字符串中包含转义符，不进入查询
                    for char in ESCAPE_CHARACTER:
                        if char in keyword:
                            flag = True
                            break
                    if not flag:
                        search_dict_of_keyword[related_field_name + '__iregex'] = keyword
                elif 'Foreign' in field_type:
                    self.get_search_factor_foreign_key(field.related_model, related_field, related_field_name, keyword,
                                                       search_dict_of_keyword, contain, regex)

    def pre_process(self, data, is_check=False):
        """
        将外键唯一性转化为外键的ID，用于保存
        :param data :[{'name': '路由器003', 'asset_sn': 'a122243545', 'dev_sn': 'a1233344345', 'production_state': '库存',
             'asset_type': '交换机'},{'name': '路由器003', 'asset_sn': 'a122243545', 'dev_sn': 'a1233344345', 'production_state': '库存',
             'asset_type': '交换机'}]
        :param is_check:是否用于校验
        :return:
        """
        if not self.batch_create_model_name or not self.batch_create_app_name:
            return data
        some_obj = apps.get_model(self.batch_create_app_name, self.batch_create_model_name)  # Asset实例对象
        for some_model_dict in data:
            if is_check:
                some_model_dict['validators'] = list()
            for field in some_obj._meta.fields:
                # 处理choices

                self.pre_process_choices(field, some_model_dict, is_check)
                # 处理外键
                self.pre_process_foreign_key(field, some_model_dict, is_check)

                if is_check:
                    # 处理唯一性
                    self.check_unique(some_obj, field, some_model_dict, data)
                    # 处理必填项
                    if field.name in self.batch_check_required_fields:
                        self.check_null(field, some_model_dict)
                # 处理特殊字段
                self.pre_special_field(field, some_model_dict, is_check)
        ZetViewSet._f_set_dicts = dict()
        ZetViewSet._unique_key_dict = dict()
        return data

    def pre_process_choices(self, field, model_dict, is_check=False):
        if field.choices:
            if field.name in model_dict.keys():
                val = model_dict[field.name]
                if val:
                    is_ok = False
                    for alp, value in field.choices:
                        if val == value:
                            model_dict[field.name] = alp
                            is_ok = True
                    if is_check:
                        if not is_ok:
                            self.record_check_log(field.name, val, CHECK_CHOICES, model_dict['validators'])

    def pre_process_foreign_key(self, field, model_dict, is_check=False):
        if field.many_to_one:
            if field.name in model_dict.keys():
                val = model_dict[field.name]
                if val:
                    is_ok = False
                    if field.related_model._meta.object_name not in ZetViewSet._f_set_dicts:
                        f_set = field.related_model.objects.all()
                        ZetViewSet._f_set_dicts[field.related_model._meta.object_name] = f_set
                    else:
                        f_set = ZetViewSet._f_set_dicts[field.related_model._meta.object_name]

                    f_fields = field.related_model._meta.fields
                    for f_field in f_fields:
                        if f_field.unique and not is_ok:
                            for f in f_set:
                                f_unique = getattr(f, f_field.name)
                                if str(f_unique) == val:
                                    model_dict[field.name] = str(f.id)
                                    is_ok = True
                                    break
                    if is_check:
                        if not is_ok:
                            self.record_check_log(field.name, val, CHECK_FOREIGN_KEY, model_dict['validators'])

    def check_unique(self, some_obj, field, model_dict, data_list):
        if field.unique or field.name in self.batch_check_unique_fields:
            if field.name in model_dict.keys():
                val = model_dict[field.name]
                if val:
                    if field.name not in ZetViewSet._unique_key_dict:
                        obj_list = some_obj.objects.values(field.name)
                        values = list()
                        for obj in obj_list:
                            value = obj[field.name]
                            values.append(value)
                        ZetViewSet._unique_key_dict[field.name] = values
                    if ListHandler.count(data_list, lambda m: m[field.name] == val) > 1:
                        self.record_check_log(field.name, val, CHECK_UNIQUE, model_dict['validators'])

                    if val in ZetViewSet._unique_key_dict[field.name]:
                        self.record_check_log(field.name, val, CHECK_UNIQUE, model_dict['validators'])

    def check_null(self, field, model_dict):
        if field.name in model_dict.keys():
            val = model_dict[field.name]
            if not val:
                self.record_check_log(field.name, val, CHECK_NOT_NULL, model_dict['validators'])
        else:
            if not field.primary_key:
                self.record_check_log(field.name, None, CHECK_NOT_NULL, model_dict['validators'])

    def pre_special_field(self, field, model_dict, is_check=False):

        # 处理IP地址
        if 'IPAddress' in str(type(field)):
            if is_check:
                self.check_ip(field, model_dict)
        # 预处理bool值
        if 'Boolean' in str(type(field)):
            value = model_dict.get(field.name)
            if value:
                if is_check:
                    if not is_boolean(value):
                        self.record_check_log(field.name, value, CHECK_BOOLEAN, model_dict['validators'])

                model_dict[field.name] = Converter.convert_to_boolean(value)

    def check_ip(self, field, model_dict):
        if field.name in model_dict.keys():
            val = model_dict[field.name]
            if val:
                if not ip_validator(val):
                    self.record_check_log(field.name, val, CHECK_IP_ADDRESS, model_dict['validators'])

    @action(detail=True, methods=['post'])
    def batch_create(self, request, *args, **kwargs):
        """批量入库"""
        try:
            # if not ZetViewSet._pre_process_res:
            #     obj_list = self.pre_process(request.data, is_check=False)
            # else:
            #     obj_list = ZetViewSet._pre_process_res
            obj_list = self.pre_process(request.data, is_check=False)
            all_count = len(obj_list)
            success_count = 0
            fail_count = 0
            err_list = []
            log = BatchImportLog()
            for obj in obj_list:
                try:
                    serializer = self.get_serializer(data=obj)
                    serializer.is_valid(raise_exception=True)
                    self.perform_create(serializer)
                    log.response_data.append(serializer.data)
                    success_count += 1
                except ValidationError as ex:
                    logger_default.error(traceback.format_exc())
                    fail_count += 1
                    err_detail = ex.detail
                    err_detail[self.batch_create_identify_code] = obj[self.batch_create_identify_code]
                    err_list.append(err_detail)
            code = status.HTTP_200_OK
            message = 'success'
            errors = []
            log.all_count = all_count
            log.success_count = success_count
            log.fail_count = fail_count
            log.err_msg = err_list
            data = log.obj_2_json(log)
        except Exception as e:
            logger_error.error(traceback.format_exc())
            code = status.HTTP_500_INTERNAL_SERVER_ERROR
            data = []
            message = 'failed:{}'.format(e)
            errors = str(e)
        resp = {'code': code,
                'message': message,
                'data': data,
                'errors': errors
                }
        return Response(resp, status=code)

    @action(detail=True, methods=['post'], url_path='check')
    def check_input(self, request, *args, **kwargs):
        """批量校验"""
        try:
            obj_list = self.pre_process(copy.deepcopy(request.data), is_check=True)
            obj_list, model_obj = self._add_verbose_name(obj_list)
            for i in range(len(request.data)):
                request.data[i]['validators'] = obj_list[i]['validators']
            code = status.HTTP_200_OK
            message = 'success'
            for obj in request.data:
                errors = json.loads(obj.get('validators'))
                if len(errors) > 0:
                    message = 'fail'

            if message == 'success':
                ZetViewSet._pre_process_res = obj_list
            else:
                ZetViewSet._pre_process_res = None

            data = request.data
            errors = []
        except Exception as e:
            logger_default.error(traceback.format_exc())
            code = status.HTTP_500_INTERNAL_SERVER_ERROR
            data = []
            message = 'failed:{}'.format(e)
            errors = str(e)

        resp = {'code': code,
                'message': message,
                'data': data,
                'errors': errors
                }
        return Response(resp, status=status.HTTP_200_OK)

    def _add_verbose_name(self, obj_list):
        """
        给校验结果中添加中文字段名称
        :param obj_list:
        :return:
        """
        some_obj = apps.get_model(self.batch_create_app_name, self.batch_create_model_name)  # Asset实例对象
        for obj in obj_list:
            errors = obj.get('validators')
            new_errors = []
            for error in errors:
                check_log = json.loads(error)
                field_name = check_log.get('field_name')

                for field in some_obj._meta.fields:
                    if field.name == field_name:
                        check_log['verbose_name'] = field.verbose_name

                # error = json.dumps(check_log, ensure_ascii=False)
                new_errors.append(check_log)
            obj['validators'].clear()
            s = json.dumps(new_errors, ensure_ascii=False)
            obj['validators'] = s
        return obj_list, some_obj

    def retrieve(self, request, *args, **kwargs):
        """
       确保返回值格式统一
       """
        response = super().retrieve(request, *args, **kwargs)
        return Response(ReturnMsg(data=response.data, code=response.status_code, message='查询成功').obj_2_json(),
                        status=response.status_code)

    def create(self, request, *args, **kwargs):
        """
        确保返回值格式统一
        """
        response = super().create(request, *args, **kwargs)
        return Response(ReturnMsg(data=response.data, code=response.status_code, message='创建成功').obj_2_json(),
                        status=response.status_code)

    def update(self, request, *args, **kwargs):
        """
        确保返回值格式统一
        """
        response = super().update(request, *args, **kwargs)
        return Response(ReturnMsg(data=response.data, code=response.status_code, message='修改成功').obj_2_json(),
                        status=response.status_code)

    def destroy(self, request, *args, **kwargs):
        """
          确保返回值格式统一
        """
        response = super().destroy(request, *args, **kwargs)
        return Response(ReturnMsg(data=response.data, code=response.status_code, message='删除成功').obj_2_json(),
                        status=response.status_code)


def exception_handler(exc, context):
    """
    自定义错误处理
    """
    if isinstance(exc, exceptions.APIException):
        headers = {}
        if getattr(exc, 'auth_header', None):
            headers['WWW-Authenticate'] = exc.auth_header
        if getattr(exc, 'wait', None):
            headers['Retry-After'] = '%d' % exc.wait

        if isinstance(exc.detail, (list, dict)):
            if isinstance(exc.detail, list):
                errors = exc.detail
            else:
                errors = {k: v[0] for k, v in exc.detail.items()}
        else:
            errors = exc.detail

        set_rollback()
        code = exc.status_code
        message = '操作失败'
        response_data = ReturnMsg(code=code, message=message, errors=errors).obj_2_json()
        logger_error.error(response_data)
        return Response(response_data, status=code, headers=headers)

    elif isinstance(exc, Http404):
        msg = _('页面不存在')
        data = {'详情': msg}

        set_rollback()
        return Response(ReturnMsg(code=status.HTTP_404_NOT_FOUND, message=msg, errors=data).obj_2_json(),
                        status=status.HTTP_404_NOT_FOUND)

    elif isinstance(exc, PermissionDenied):
        msg = _('没有权限，拒绝访问！')
        data = {'详情': msg}

        set_rollback()
        return Response(ReturnMsg(code=status.HTTP_403_FORBIDDEN, message=msg, errors=data).obj_2_json(),
                        status=status.HTTP_403_FORBIDDEN)
    return None
